//
//  Tools.swift
//  YueChat
//
//  Created by IMac on 2024/9/12.
//

import UIKit
import AVFoundation

// MARK:  全局通知
let kAudioNot_STATE       =                             "kAudioNot_STATE"                          // 语音状态变化
let kUpdateConversation      =             "kUpdateConversation"                     // 更新会话状态变化
let kDeleteUserChanged              =                 "kDeleteUserChanged"                           // 删除用户状态变化
let kGroupUpdateChanged         =                    "kGroupUpdateChanged"                         // 群组刷新状态变化
let kMsgStatusChanged            =                    "kMsgStatusChanged"                            // 未读状态变化
let kUpdateChatMessage               =                 "kUpdateChatMessage"                            // 标签变化


// 判断是否为iPhone设备
let isiPhone = UIDevice.current.userInterfaceIdiom == .phone

// 获取屏幕尺寸
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height

// 判断是否为iPhone X系列（刘海屏）
let isiPhoneX = screenWidth >= 375.0 && screenHeight >= 812.0 && isiPhone

// 获取状态栏高度
let statusHeight = UIApplication.shared.statusBarFrame.size.height

let KUserDefaults = UserDefaults.standard
//let kGrayBG = UIColor(red: 244/255, green: 244/255, blue: 244/255, alpha: 1)
//let kGrayBG = UIColor(hex: 0xededed)
let kGrayBG = UIColor(hex: 0xF4F7FB)


let kMainColor = UIColor(hex: 0x566796)
let kBlue = UIColor(hex: 0x52A5FF)


// 计算各种高度值
let iPhoneXBottomHeight = isiPhoneX ? 34.0 : 0.0
let iPhoneXTopHeight = isiPhoneX ? 34.0 : 0.0

let GKNavHeight = isiPhoneX ? statusHeight + 44 : 64
let iPhoneXNavStatus = isiPhoneX ? 44.0 : 20.0
let tabBarHeight = isiPhoneX ? (49.0 + 34.0) : 49.0

// 计算安全区域高度
let safeHeight = screenHeight - statusHeight - iPhoneXBottomHeight

// 计算屏幕可用高度（减去导航栏和底部安全区域）
let screenUsefulHeight = screenHeight - GKNavHeight - iPhoneXBottomHeight

// 屏幕宽度比例
let screenRatioWidth = screenWidth / 360

// 子视图高度（减去导航栏和底部安全区域，以及额外的40像素）
let childHeight = screenHeight - GKNavHeight - 40

// 安全区域底部高度
let safeBottomHeight = isiPhoneX ? 34.0 : 0.0

// 导航栏高度
let navHeight = isiPhoneX ? 0 : 64.0

// 导航栏内容区域高度
let navContentHeight = isiPhoneX ? 0 : 50

// 屏幕顶部高度
let topHeight = isiPhoneX ? statusHeight : statusHeight

// 聊天发送视图底部高度
let chatSendViewBottom = isiPhoneX ? 23 : 0

// Tab Bar高度
let kTabBarHeightConstant = isiPhoneX ? (49.0 + 34.0) : 49.0

class Tools: NSObject {

}

func isPhoneX() -> Bool {
    guard #available(iOS 11.0, *) else {
        return false // iOS 11之前没有刘海屏设备
    }
    
    if let safeArea = UIApplication.shared.keyWindow?.safeAreaInsets.top, safeArea > 0 {
        return true
    }
    
    return false
}


var kUserImage : UIImage = {
    let kUserImage = UIImage(named: "headImg")
   
    return kUserImage!
}()

var kGroupImage : UIImage = {
    let kUserImage = UIImage(named: "groupImg")
   
    return kUserImage!
}()

var SDImageDefault : UIImage = {
    let SDImageDefault = UIImage(named: "headImg")
   
    return SDImageDefault!
}()

func GK_main_async_safe(block: @escaping () -> Void) {
    if Thread.isMainThread {
        block()
    } else {
        DispatchQueue.main.async {
            block()
        }
    }
}

func HexColor(_ hex: UInt) -> UIColor {
    let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0
    let g = CGFloat((hex & 0x00FF00) >> 8) / 255.0
    let b = CGFloat(hex & 0x0000FF) / 255.0
    return UIColor(red: r, green: g, blue: b, alpha: 1.0)
}

func getHomeFilePath(localPath: String) -> String {
    if FileManager.default.fileExists(atPath: localPath) {
        return localPath
    } else {
        if let location = localPath.range(of: "Containers/Data/Application/")?.lowerBound {
            let startIndex = localPath.index(location, offsetBy: 0)
            let endIndex = localPath.index(localPath.endIndex, offsetBy: -1)
            _ = startIndex...endIndex
            
            let documentsRange = localPath.range(of: "Documents", options: .caseInsensitive, range: location..<localPath.endIndex)
            let libraryRange = localPath.range(of: "Library", options: .caseInsensitive, range: location..<localPath.endIndex)
            let tmpRange = localPath.range(of: "tmp", options: .caseInsensitive, range: location..<localPath.endIndex)

            let minLocation = [
                documentsRange?.lowerBound,
                libraryRange?.lowerBound,
                tmpRange?.lowerBound
            ].compactMap { $0 }.min() ?? localPath.endIndex
            
            if minLocation != endIndex {
                let relativePath = String(localPath[minLocation...])
                let path = NSHomeDirectory().appending("/\(relativePath)")
                return path
            }
        }
        return localPath
    }
}

func formatLocalUrl(filePath: String) -> String? {
    if filePath.hasPrefix("file://") {
        
        let p = filePath.replacingOccurrences(of: "file://", with: "")
//            path.absoluteString = path.absoluteString.replacingOccurrences(of: "file://", with: "")
        return p
    }
    return filePath
}

// 格式化时间标签
func formatTimeLabel(_ timestamp: Int64) -> String? {
    guard timestamp != 0 else {
        return nil
    }
    
    let date = Date(timeIntervalSince1970: Double(timestamp) / 1000)
    let current = Date()
    let calendar = Calendar.current
    
    let years = calendar.component(.year, from: date)
    let curYears = calendar.component(.year, from: current)
    
    if calendar.isDateInToday(date) {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        return formatter.string(from: date)
    } else if calendar.isDateInYesterday(date) {
        return KLocalizedString("DataToolsY")
    } else {
        if years == curYears {
            let weeksOfYear = calendar.component(.weekOfYear, from: date)
            let curWeeksOfYear = calendar.component(.weekOfYear, from: current)
            
            let weekDay = calendar.component(.weekday, from: date)
            if weeksOfYear == curWeeksOfYear {
                switch weekDay {
                case 1:
                    return KLocalizedString("DataToolsSev")
                case 2:
                    return KLocalizedString("DataToolsOne")
                case 3:
                    return KLocalizedString("DataToolsTwo")
                case 4:
                    return KLocalizedString("DataToolsThr")
                case 5:
                    return KLocalizedString("DataToolsFor")
                case 6:
                    return KLocalizedString("DataToolsFive")
                case 7:
                    return KLocalizedString("DataToolsSix")
                default:
                    return "\(weekDay)"
                }
            } else {
                let month = calendar.component(.month, from: date)
                let day = calendar.component(.day, from: date)
                return "\(month)/\(day)"
            }
        } else {
            let month = calendar.component(.month, from: date)
            let day = calendar.component(.day, from: date)
            return "\(years)/\(month)/\(day)"
        }
    }
}

 func formatTimeDetailLabel(timestamp: Int64) -> String? {
    guard timestamp != 0 else {
        return nil
    }
    
    let date = Date(timeIntervalSince1970: Double(timestamp) / 1000)
    
    let current = Date()
    let calendar = Calendar.current
    
    let months = calendar.component(.month, from: date)
    let curMonths = calendar.component(.month, from: current)
    let years = calendar.component(.year, from: date)
    let curYears = calendar.component(.year, from: current)
    
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm"
    let hourTimeStr = formatter.string(from: date)
    
    let weeks = calendar.component(.weekOfYear, from: date)
    let curWeeks = calendar.component(.weekOfYear, from: current)
    
    let weekDays = calendar.component(.weekday, from: date)
    
    if calendar.isDateInToday(date) {
        return hourTimeStr
    } else if calendar.isDateInYesterday(date) {
        return NSLocalizedString("DataToolsY", comment: "") + " " + hourTimeStr
    } else if years != curYears {
        formatter.dateFormat = "yyyy/MM/dd HH:mm"
        return formatter.string(from: date)
    } else if months != curMonths {
        if weeks == curWeeks {
            return formatWeek(weekDay: weekDays)! + " " + hourTimeStr
        }
        
        formatter.dateFormat = "MM/dd HH:mm"
        return formatter.string(from: date)
    } else {
        if weeks == curWeeks {
            return formatWeek(weekDay: weekDays)! + " " + hourTimeStr
        }
        formatter.dateFormat = "MM/dd HH:mm"
        return formatter.string(from: date)
    }
}

func formatWeek(weekDay: Int) -> String? {
    let normalizedWeekDay = weekDay % 7
    switch normalizedWeekDay {
    case 2:
        return NSLocalizedString("DataToolsOne", comment: "")
    case 3:
        return NSLocalizedString("DataToolsTwo", comment: "")
    case 4:
        return NSLocalizedString("DataToolsThr", comment: "")
    case 5:
        return NSLocalizedString("DataToolsFor", comment: "")
    case 6:
        return NSLocalizedString("DataToolsFive", comment: "")
    case 0:
        return NSLocalizedString("DataToolsSix", comment: "")
    case 1:
        return NSLocalizedString("DataToolsSev", comment: "")
    default:
        return nil
    }
}

func cellTextHeight(_ str: String, font fontSice: CGFloat, textWith width: CGFloat) -> CGFloat {
    guard !str.isEmpty else {
        return 0
    }
    
    let attributeString = NSMutableAttributedString(string: str)
    let style = NSMutableParagraphStyle()
    style.lineSpacing = 1
    
    let font = UIFont.systemFont(ofSize: fontSice)
    attributeString.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: str.utf16.count))
    attributeString.addAttribute(.font, value: font, range: NSRange(location: 0, length: str.utf16.count))
    
    let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
    let rect = attributeString.boundingRect(with: CGSize(width: width, height: CGFLOAT_MAX), options: options, context: nil)
    return rect.size.height
}

func cellTextWidth(for str: String, font: CGFloat, textHeight: CGFloat) -> CGFloat {
    let attributeString = NSMutableAttributedString(string: str)
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineSpacing = 1
    
    let fontObj = UIFont.systemFont(ofSize: font)
    attributeString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: str.utf16.count))
    attributeString.addAttribute(.font, value: fontObj, range: NSRange(location: 0, length: str.utf16.count))
    
    let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
    let rect = attributeString.boundingRect(with: CGSize(width: CGFLOAT_MAX, height: textHeight), options: options, context: nil)
    
    return rect.size.width
}

func imageScale(imageSize: CGSize, targetSize: CGSize, thumbnailPoint: inout CGPoint?) -> CGSize {
    if imageSize.width == 0 && imageSize.height == 0 {
        return targetSize
    }
    
    let imageWidth = imageSize.width
    let imageHeight = imageSize.height
    let targetWidth = targetSize.width
    let targetHeight = targetSize.height
    
    var scaleFactor: CGFloat = 0.0
    var scaledWidth: CGFloat = 0.0
    var scaledHeight: CGFloat = 0.0
    
    if imageWidth / imageHeight < 2.4 && imageHeight / imageWidth < 2.4 {
        let widthFactor = targetWidth / imageWidth
        let heightFactor = targetHeight / imageHeight
        
        scaleFactor = widthFactor < heightFactor ? widthFactor : heightFactor
        scaledWidth = imageWidth * scaleFactor
        scaledHeight = imageHeight * scaleFactor
        
        if widthFactor > heightFactor {
            thumbnailPoint?.y = (targetHeight - scaledHeight) * 0.5
        } else if widthFactor < heightFactor {
            thumbnailPoint?.x = (targetWidth - scaledWidth) * 0.5
        }
    } else {
        if imageWidth / imageHeight > 2.4 {
            scaleFactor = 100 * targetHeight / imageHeight / 240
        } else {
            scaleFactor = 100 * targetWidth / imageWidth / 240
        }
        scaledWidth = imageWidth * scaleFactor
        scaledHeight = imageHeight * scaleFactor
    }
    return CGSize(width: scaledWidth, height: scaledHeight)
}

func getImageSizeForArea(msgModel: DMCCMessage, withViewWidth width: CGFloat) -> CGSize {
    guard let imgContent = msgModel.content as? OsnImageMessageContent else {
        return CGSize(width: 80, height: 143)
    }
    
    var size = CGSize(width: 150, height: 150)
    
    if imgContent.width == 0 {
        imgContent.width = 80
        imgContent.height = 143
        //imgContent.size = CGSize(width: 80, height: 143)
    }
    
//    if let thumbnail = imgContent.thumbnail {
//        size = thumbnail.size
//    } else {
        var thumbnailPoint: CGPoint?
        size = imageScale(imageSize: CGSize(width: imgContent.width, height: imgContent.height), targetSize: CGSize(width: 150, height: 150), thumbnailPoint: &thumbnailPoint)
//    }
    
    if size.height > width || size.width > width {
        let scale = min(width / size.height, width / size.width)
        size = CGSize(width: size.width * scale, height: size.height * scale)
    }
    
    if size.width.isNaN {
        return CGSize(width: 80, height: 143)
    }
    
    return size
}

func getFilePath(localPath: String) -> String {
    guard !localPath.isEmpty else {
        return ""
    }
    
    if FileManager.default.fileExists(atPath: localPath) {
        return localPath
    } else {
        let applicationPrefix = "Containers/Data/Application/"
//        let applicationPrefixLength = applicationPrefix.count
        
        guard let applicationStartIndex = localPath.range(of: applicationPrefix)?.lowerBound else {
            return localPath
        }
        
        let remainingPath = String(localPath[applicationStartIndex...])
        
        let documentsStartIndex = remainingPath.range(of: "Documents", options: .caseInsensitive)?.lowerBound ?? remainingPath.endIndex
        let libraryStartIndex = remainingPath.range(of: "Library", options: .caseInsensitive)?.lowerBound ?? remainingPath.endIndex
        let tmpStartIndex = remainingPath.range(of: "tmp", options: .caseInsensitive)?.lowerBound ?? remainingPath.endIndex
        
        let minStartIndex = [documentsStartIndex, libraryStartIndex, tmpStartIndex].min() ?? remainingPath.endIndex
        
        if minStartIndex != remainingPath.endIndex {
            let relativePath = String(remainingPath[minStartIndex...])
            return "\(NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!)\(relativePath)"
        } else {
            return localPath
        }
    }
}

func getFileThumbnailImage(from videoPath: String) -> UIImage? {
    let url = URL(fileURLWithPath: videoPath)
    
    let asset = AVURLAsset(url: url)
    let assetImageGenerator = AVAssetImageGenerator(asset: asset)
    assetImageGenerator.appliesPreferredTrackTransform = true
    assetImageGenerator.apertureMode = .encodedPixels
    
    let time = 0.1
    var thumbnailImageRef: CGImage?
    do {
        thumbnailImageRef = try assetImageGenerator.copyCGImage(at: CMTimeMake(value: Int64(time), timescale: 60), actualTime: nil)
    } catch let error {
        print(error.localizedDescription)
        return nil
    }
    
    return thumbnailImageRef != nil ? UIImage(cgImage: thumbnailImageRef!) : nil
}


// 检查是否有访问麦克风权限
func haveAVAuthorizationStatusCode() -> Bool {
    let authStatus = AVCaptureDevice.authorizationStatus(for: .audio)
    switch authStatus {
    case .restricted, .denied:
        MHAlert.showMessage(KLocalizedString("AlertMicroNoFind"))
        return false
    case .notDetermined:
        AVCaptureDevice.requestAccess(for: .audio) { granted in
            if granted {
                print("Authorized")
            } else {
                print("Denied or Restricted")
            }
        }
        return true
    case .authorized:
        return true
    @unknown default:
        fatalError("Unknown AVCaptureDeviceAuthorizationStatus")
    }
}


func getRandString(withLength length: Int) -> String {
    let sourceStr = "abcdefghijklmnopqrstuvwxyz"
    var resultStr = ""

    for _ in 0..<length {
        let index = Int(arc4random_uniform(UInt32(sourceStr.count)))
        let oneChar = sourceStr[sourceStr.index(sourceStr.startIndex, offsetBy: index)]
        resultStr.append(oneChar)
    }

    return resultStr
}

// 获取缓存路径
    func cachePath(of conversation: DMCCConversation, mediaType: OsnMediaType) -> String {
        let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        
        let mediaTypeString: String
        switch mediaType {
        case .image:
            mediaTypeString = "image"
        case .voice:
            mediaTypeString = "voice"
        case .video:
            mediaTypeString = "video"
        case .portrait:
            mediaTypeString = "portrait"
        case .favorite:
            mediaTypeString = "favorite"
        case .sticker:
            mediaTypeString = "sticker"
        case .moments:
            mediaTypeString = "moments"
        default:
            mediaTypeString = "general"
        }
        let targetid = conversation.targetId
        let path = documentDirectory.appendingPathComponent("ConversationResourc/\(conversation.type)/\(targetid)/").appendingPathComponent(mediaTypeString)
        if path.absoluteString.hasPrefix("file://") {
            
            let p = path.absoluteString.replacingOccurrences(of: "file://", with: "")
//            path.absoluteString = path.absoluteString.replacingOccurrences(of: "file://", with: "")
            
            do {
                try FileManager.default.createDirectory(atPath: p, withIntermediateDirectories: true)
//                FileManager.default.createDirectory(at: path, withIntermediateDirectories: true, attributes: nil)
            } catch {
                print("Failed to create directory at path: \(path) due to error: \(error)")
            }
            return p
        }
        return ""
    }

// 获取缓存路径
    func cachePathOf(_ conversation: DMCCConversation, mediaType: OsnMediaType) -> String {
        let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
//        var path = documentDirectory.appendingPathComponent(conversation.conversationFilesDir, conformingTo: mediaType)
        let  path = documentDirectory.appendingPathComponent("\(conversation.type)/\(String(conversation.targetId))")
        
        var type = "general"
        switch mediaType {
        case .image:
            type = "image"
        case .voice:
            type = "voice"
        case .video:
            type = "video"
        case .portrait:
            type = "portrait"
        case .favorite:
            type = "favorite"
        case .sticker:
            type = "sticker"
        case .moments:
            type = "moments"
        case .general:
            break
        case .file:
            break
        case .transfer:
            break
        @unknown default:
            break
        }
        
        
        var filePath = path.absoluteString
        filePath.append(type)
        
        if filePath.hasPrefix("file://") {
            
            let p = filePath.replacingOccurrences(of: "file://", with: "")
//            path.absoluteString = path.absoluteString.replacingOccurrences(of: "file://", with: "")
            
            do {
                try FileManager.default.createDirectory(atPath: p, withIntermediateDirectories: true)
//                FileManager.default.createDirectory(at: path, withIntermediateDirectories: true, attributes: nil)
            } catch {
                print("Failed to create directory at path: \(path) due to error: \(error)")
            }
            return p
        }
        
        return path.absoluteString
    }

func getVideoInfo(at path: URL, completion: @escaping (UIImage?, CMTime?) -> Void) {
    // 1. 创建一个AVAsset对象
    let asset = AVAsset(url: path)
    
    // 2. 获取视频时长
    let duration = asset.duration
    
    // 3. 提取第一张图片
    let imageGenerator = AVAssetImageGenerator(asset: asset)
    imageGenerator.appliesPreferredTrackTransform = true
    
    let time = CMTimeMake(value: 0, timescale: asset.duration.timescale)
    
    do {
            let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
            let thumbnail = UIImage(cgImage: cgImage)
            completion(thumbnail, duration)
        } catch {
            completion(nil, duration)
        }
}

func  imageWithColor(_ color: UIColor) -> UIImage {
        let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
        
        // 开启位图上下文
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
        
        // 使用颜色填充上下文
        color.setFill()
        UIRectFill(rect)
        
        // 从上下文中获取图片
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        // 结束上下文
        UIGraphicsEndImageContext()
        
        return image ?? UIImage()
    }

//MARK: Color

extension UIColor {
    convenience init(hex: Int) {
        let red = (hex & 0xFF0000) >> 16
        let green = (hex & 0x00FF00) >> 8
        let blue = hex & 0x0000FF
        self.init(red: CGFloat(red) / 255.0,
                  green: CGFloat(green) / 255.0,
                  blue: CGFloat(blue) / 255.0,
                  alpha: 1.0)
    }
    
    static var MainColor : UIColor {
        return UIColor(hex: 0x566796)
    }
}


// 一些常用的计算属性
extension UIScreen {
    static var safeBottom: CGFloat {
        return isiPhoneX ? 34 : 0
    }
    
    static var safeTop: CGFloat {
        return statusHeight
    }
    
    static var safeHeight: CGFloat {
        return UIScreen.main.bounds.height - statusHeight - UIScreen.safeBottom
    }
    
    static var usefulHeight: CGFloat {
        return UIScreen.main.bounds.height - GKNavHeight - UIScreen.safeBottom
    }
    
    static var ratioWidth: CGFloat {
        return UIScreen.main.bounds.width / 360
    }
    
    static var childHeight: CGFloat {
        return UIScreen.main.bounds.height - GKNavHeight - 40
    }
    
    static var tabBarHeight: CGFloat {
        return isiPhoneX ? (49.0 + 34.0) : 49.0
    }

}

extension String {
    func toJson() -> [String: Any]? {
        guard let data = self.data(using: .utf8) else {
            // 如果无法将字符串转换为 UTF-8 数据，则返回 nil
            return nil
        }
          
        do {
            if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                return dict
            }
            // 如果 JSON 不是字典类型，则隐式返回 nil
        } catch {
            print("Failed to decode JSON string: \(error.localizedDescription)")
            // 打印错误，但按照当前实现，我们仍然返回 nil
        }
          
        return nil
    }
}

extension Date {
    static func getCurrentTime() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let currentTimeString = formatter.string(from: Date())
        return currentTimeString
    }
    
    static func timeStamp(withDateString dateString: String) -> TimeInterval {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        if let date = formatter.date(from: dateString) {
            return date.timeIntervalSince1970 * 1000 // 返回毫秒级时间戳
        } else {
            return 0 // 或者抛出错误，根据具体需求处理
        }
    }
}


